﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using OpenTK;
using OpenTK.Windowing.Desktop;
using OpenTK.Windowing.Common;
using OpenTK.Graphics.OpenGL;
using OpenTK.Mathematics;
using System.ComponentModel;
using System.Timers;
using OpenTK.Windowing.GraphicsLibraryFramework; 

namespace OpenTKBasics
{
    class Game : GameWindow
    {
        private int vertexBuffHandle;       //顶点缓存句柄   VBO
        private int vertexArrayHandle;      //顶点列表句柄   VAO句柄

        private Shader Shader;
        private Camera camera;

        double timeValue = 0.0d;

        Matrix4 transform;
        float aspect = 2.0f;


        public Game(int width = 1920, int height = 1080, string title = "GAME1")
            : base(
                  GameWindowSettings.Default,
                  new NativeWindowSettings()
                  {
                      Title = title,
                      Size = new Vector2i(width, height),
                      WindowBorder = WindowBorder.Fixed,
                      StartVisible = false,
                      StartFocused = true,
                      API = ContextAPI.OpenGL,
                      Profile = ContextProfile.Core,
                      APIVersion = new Version(3, 3),
                  })
        {
            this.CenterWindow();

            this.Resize += WindowResize;
            this.Load += WindowLoad;
            this.Unload += WindowUnload;
            this.RenderFrame += WindowRenderFrame;
            this.UpdateFrame += WindowUpdateFrame;
            this.Closing += WindowClosing;
             
        }

        void WindowResize(ResizeEventArgs e)
        {
            aspect = e.Width / (float)e.Height;
            GL.Viewport(0, 0, e.Width, e.Height);
            GL.MatrixMode(MatrixMode.Projection);
            GL.LoadIdentity();//RESET TO IDENT
            GL.Ortho(0.0, 50.0, 0.0, 50.0, -1.0, 1.0);
            GL.MatrixMode(MatrixMode.Modelview);
            // base.OnResize(e);
        }

        void WindowLoad()
        {
            this.IsVisible = true;

            //模型变换矩阵
            Matrix4 scale = Matrix4.CreateScale(0.5f, 0.5f, 0.5f);
            Matrix4 rotation = Matrix4.CreateRotationZ(MathHelper.DegreesToRadians(10.0f));
            Matrix4 trans = Matrix4.CreateTranslation(0.0f, 1.0f, 0.0f);
            transform = trans * rotation * scale;

            //加载shader文件
            this.Shader = new Shader("./shader/shader.vert", "./shader/shader.frag");
            Shader.Use();

            GL.ClearColor(Color4.AliceBlue);
            //构建三角形的点集
            //float[] vertices = new float[] {
            //    0.0f,-0.5f,0.0f,   1.0f,0.0f,0.0f,  1.0f,
            //    1.8f,-0.0f,0.0f,   0.0f,1.0f,0.0f,  1.0f,
            //    -0.0f,0.5f,0.0f,   0.0f,0.0f,1.0f,  1.0f,
            //}; 
            //构成立方体的点集
            float[] vertices = {
                -0.5f, -0.5f, -0.5f,  0.0f, 0.0f, 1.0f, 1.0f,
                 0.5f, -0.5f, -0.5f,  1.0f, 0.0f, 1.0f, 1.0f,
                 0.5f,  0.5f, -0.5f,  1.0f, 1.0f, 1.0f, 1.0f,
                 0.5f,  0.5f, -0.5f,  1.0f, 1.0f, 1.0f, 1.0f,
                -0.5f,  0.5f, -0.5f,  0.0f, 1.0f, 1.0f, 1.0f,
                -0.5f, -0.5f, -0.5f,  0.0f, 0.0f, 1.0f, 1.0f,

                -0.5f, -0.5f,  0.5f,  0.0f, 0.0f, 1.0f, 1.0f,
                 0.5f, -0.5f,  0.5f,  1.0f, 0.0f, 1.0f, 1.0f,
                 0.5f,  0.5f,  0.5f,  1.0f, 1.0f, 1.0f, 1.0f,
                 0.5f,  0.5f,  0.5f,  1.0f, 1.0f, 1.0f, 1.0f,
                -0.5f,  0.5f,  0.5f,  0.0f, 1.0f, 1.0f, 1.0f,
                -0.5f, -0.5f,  0.5f,  0.0f, 0.0f, 1.0f, 1.0f,

                -0.5f,  0.5f,  0.5f,  1.0f, 0.0f, 1.0f, 1.0f,
                -0.5f,  0.5f, -0.5f,  1.0f, 1.0f, 1.0f, 1.0f,
                -0.5f, -0.5f, -0.5f,  0.0f, 1.0f, 1.0f, 1.0f,
                -0.5f, -0.5f, -0.5f,  0.0f, 1.0f, 1.0f, 1.0f,
                -0.5f, -0.5f,  0.5f,  0.0f, 0.0f, 1.0f, 1.0f,
                -0.5f,  0.5f,  0.5f,  1.0f, 0.0f, 1.0f, 1.0f,

                 0.5f,  0.5f,  0.5f,  1.0f, 0.0f, 1.0f, 1.0f,
                 0.5f,  0.5f, -0.5f,  1.0f, 1.0f, 1.0f, 1.0f,
                 0.5f, -0.5f, -0.5f,  0.0f, 1.0f, 1.0f, 1.0f,
                 0.5f, -0.5f, -0.5f,  0.0f, 1.0f, 1.0f, 1.0f,
                 0.5f, -0.5f,  0.5f,  0.0f, 0.0f, 1.0f, 1.0f,
                 0.5f,  0.5f,  0.5f,  1.0f, 0.0f, 1.0f, 1.0f,

                -0.5f, -0.5f, -0.5f,  0.0f, 1.0f, 1.0f, 1.0f,
                 0.5f, -0.5f, -0.5f,  1.0f, 1.0f, 1.0f, 1.0f,
                 0.5f, -0.5f,  0.5f,  1.0f, 0.0f, 1.0f, 1.0f,
                 0.5f, -0.5f,  0.5f,  1.0f, 0.0f, 1.0f, 1.0f,
                -0.5f, -0.5f,  0.5f,  0.0f, 0.0f, 1.0f, 1.0f,
                -0.5f, -0.5f, -0.5f,  0.0f, 1.0f, 1.0f, 1.0f,

                -0.5f,  0.5f, -0.5f,  0.0f, 1.0f, 1.0f, 1.0f,
                 0.5f,  0.5f, -0.5f,  1.0f, 1.0f, 1.0f, 1.0f,
                 0.5f,  0.5f,  0.5f,  1.0f, 0.0f, 1.0f, 1.0f,
                 0.5f,  0.5f,  0.5f,  1.0f, 0.0f, 1.0f, 1.0f,
                -0.5f,  0.5f,  0.5f,  0.0f, 0.0f, 1.0f, 1.0f,
                -0.5f,  0.5f, -0.5f,  0.0f, 1.0f, 1.0f, 1.0f,
            };

            //检查允许的最大顶点组  通常为16 满足多数情况
            int nrAttributes = 0;
            GL.GetInteger(GetPName.MaxVertexAttribs, out nrAttributes);
            Console.WriteLine("Maximum number of vertex attributes supported: " + nrAttributes);

            //
            this.vertexBuffHandle = GL.GenBuffer();//建立一个顶点缓存句柄
            GL.BindBuffer(BufferTarget.ArrayBuffer, this.vertexBuffHandle);//将顶点缓存句柄绑定在缓存实例
            GL.BufferData(BufferTarget.ArrayBuffer,
                vertices.Length * sizeof(float),
                vertices, BufferUsageHint.StaticDraw);  //将顶点位置放入缓存实例
            GL.BindBuffer(BufferTarget.ArrayBuffer, 0);

            /*
             this.elementBuffHandle = GL.GenBuffer();
             GL.BindBuffer(BufferTarget.ElementArrayBuffer, elementBuffHandle);
             GL.BufferData(BufferTarget.ElementArrayBuffer, indices.Length * sizeof(uint), indices, BufferUsageHint.StaticDraw);
             GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
             */

            this.vertexArrayHandle = GL.GenVertexArray();       //顶点列表VAO
            GL.BindVertexArray(this.vertexArrayHandle);         //VAO句柄绑定进入GL状态机

            //GL.BindBuffer(BufferTarget.ArrayBuffer, this.vertexBuffHandle);
            int vertextLoc = this.Shader.GetAttribLocation("aPosition");    //获得shader内变量的layout句柄位置 
            GL.VertexAttribPointer(vertextLoc, 3, VertexAttribPointerType.Float, false, 7 * sizeof(float), 0);                   //VAO读取VBO数据的方式 0 为组号  offset表述在每段内读取的开始位
            int colorLoc = this.Shader.GetAttribLocation("aColor");
            GL.VertexAttribPointer(colorLoc, 4, VertexAttribPointerType.Float, false, 7 * sizeof(float), 3 * sizeof(float));   //VAO读取VBO数据的方式 1 为组号  offset表述在每段内读取的开始位
            GL.EnableVertexAttribArray(vertextLoc);  //上述定义的防止顶点的缓存组  组号0
            GL.EnableVertexAttribArray(colorLoc);  //上述定义的防止顶点的缓存组  组号1 

            GL.BindVertexArray(0);

            //相机生成
            camera = new Camera(3.0f * Vector3.UnitZ, aspect);
        }
        void WindowUnload()
        {
            GL.BindVertexArray(0);
            GL.DeleteVertexArray(this.vertexArrayHandle);

            GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
            GL.DeleteBuffer(this.vertexBuffHandle);

            Shader.Dispose();

            //GL.UseProgram(0);
            //GL.DeleteProgram(this.shaderProgramHandle);

            //base.OnUnload();
        }
        void WindowClosing(CancelEventArgs args)
        {
            Console.WriteLine("CLOSE");
        }

        void WindowRenderFrame(FrameEventArgs args)
        {
            //GL.ClearColor(new Color4(0.3f, 0.4f, 0.6f, 1.0f));
            GL.Clear(ClearBufferMask.ColorBufferBit);
             

            GL.BindVertexArray(this.vertexArrayHandle);
            GL.DrawArrays(PrimitiveType.Triangles, 0, 36); //指定绘制方式
            //GL.DrawElements(PrimitiveType.Triangles, indices.Length, DrawElementsType.UnsignedInt, 0);

            //GL.Rotate(theta, 0.0, 0.0, 1.0);
            //theta += 1.0f;

            this.Context.SwapBuffers();


            // base.OnRenderFrame(args);
        }

        void WindowUpdateFrame(FrameEventArgs args)
        {
            //清屏
            GL.Enable(EnableCap.DepthTest);
            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
            //base.OnUpdateFrame(args);

            //uniform shader  
            {
                //对shader传值 导致颜色变化
                timeValue += 0.1d;
                float greenValue = (float)Math.Sin(timeValue) / (2.0f + 0.5f);
                int vertexColorLocation = GL.GetUniformLocation(this.Shader.GetHandle(), "uColor");
                GL.Uniform4(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);


                //定义模型变换矩阵
                Matrix4 scale = Matrix4.CreateScale(0.5f, 0.5f, 0.5f);
                Matrix4 rotation = Matrix4.CreateRotationX(MathHelper.DegreesToRadians(100.0f));
                Matrix4 trans = Matrix4.CreateTranslation(-0.5f, 0.2f, 0.0f);
                transform = trans * rotation * scale;

                //矩阵整合进入shader 对shader传矩阵
                float rotValue = (float)Math.Sin(timeValue) * 80.0f;
                Matrix4 model = Matrix4.CreateRotationZ(MathHelper.DegreesToRadians(rotValue));
                model *= transform;
                Matrix4 view = camera.GetViewMatrix();
                Matrix4 projection = camera.GetProjectionMatrix();
                this.Shader.SetMatrix4("model", model);
                this.Shader.SetMatrix4("view", view);
                this.Shader.SetMatrix4("projection", projection);
                //Console.WriteLine("UPDATE");
            }

            //input
            {
                // Check to see if the window is focused
                if (!IsFocused) { return; }
                KeyboardState input = KeyboardState;
                if (input.IsKeyDown(Keys.Escape)) { Close(); }
                camera.KeyControllor(input, args);

                MouseState mouse = MouseState;
                camera.MouseMoveControllor(mouse);
            }
             
        }

        //void WindowOnMouseWheel(MouseWheelEventArgs e)
        //{
        //     camera.Fov -= e.OffsetY;
        //}
        protected override void OnMouseWheel(MouseWheelEventArgs e)
        {
            base.OnMouseWheel(e); 
            camera.Fov -= e.OffsetY;
        }
    }
}
